package org.d8s.magicapi.plugin.interceptor;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.time.DateUtils;
import org.d8s.magicapi.pojo.OpenAuthAccountInfo;
import org.d8s.magicapi.util.ApiStatusCodeEnum;
import org.d8s.magicapi.util.SecurityUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.ssssssss.magicapi.interceptor.RequestInterceptor;
import org.ssssssss.magicapi.model.ApiInfo;
import org.ssssssss.magicapi.model.Constants;
import org.ssssssss.magicapi.model.JsonBean;
import org.ssssssss.script.MagicScriptContext;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.d8s.magicapi.util.EncryptConstants.START_PLUGIN_LOG_MSG;


/**
 * 接口验签
 *
 * @author 冰点
 * @date 2021-5-20 15:14:59
 */
@ConditionalOnProperty(prefix = "magic-plugin.openauth", name = "enable", havingValue = "true", matchIfMissing = false)
@Component
@ConfigurationProperties(prefix = "magic-plugin.openauth")
public class OpenAuthRequestInterceptor implements RequestInterceptor {

    private static final Logger log = LoggerFactory.getLogger(OpenAuthRequestInterceptor.class);
    /**
     * 对接方appId和密钥配置
     */
    private List<OpenAuthAccountInfo> accounts;
    /**
     * 接口时间戳最大延迟 单位为秒
     */
    @Value("${magic-plugin.openauth.maxTimeDelay:300}")
    private int maxTimeDelay;
    private Map<String, OpenAuthAccountInfo> accountMap = new HashMap<>();

    @PostConstruct
    public void initIpLimitRequestInterceptor() {
        accountMap = accounts.stream().collect(Collectors.toMap(OpenAuthAccountInfo::getAppId, Function.identity(), (key1, key2) -> key1));
        log.info(START_PLUGIN_LOG_MSG, "接口验签", "magic-plugin.openauth.enable=false", "magic-plugin.openauth.accounts");
    }

    /**
     * 接口请求之前
     *
     * @param info    接口信息
     * @param context 脚本变量信息
     */
    @Override
    public Object preHandle(ApiInfo info, MagicScriptContext context, HttpServletRequest request, HttpServletResponse response) throws Exception {
        return isSignatureValid(context.getRootVariables());
    }

    private JsonBean isSignatureValid(Map<String, Object> rootVariables) {
        JsonBean jsonBean = null;
        try {
            Map<String, Object> body = (Map) rootVariables.get(Constants.VAR_NAME_REQUEST_BODY);
            Map<String, Object> header = (Map) rootVariables.get(Constants.VAR_NAME_HEADER);
            String appId = String.valueOf(body.get("appId"));
            long timestamp = Long.valueOf(String.valueOf(body.get("timestamp")));
            if (checkTimestamp(timestamp)) {
                return new JsonBean(Integer.valueOf(ApiStatusCodeEnum.API_STATUS_CODE_32.getCode()), ApiStatusCodeEnum.API_STATUS_CODE_32.getMessage());
            }
            String requestSign = String.valueOf(header.get("sign"));
            Map<String, Object> dataMap = new TreeMap<>();
            dataMap.putAll(body);
            if (accountMap.containsKey(appId)) {
                String secret = accountMap.get(appId).getAppSecret();
                String sign = SecurityUtil.generateSign(dataMap, appId, secret);
                if (!requestSign.equals(sign)) {
                    log.info("验证签名不通过requestSign={},sign={}", requestSign, sign);
                    jsonBean = new JsonBean(Integer.valueOf(ApiStatusCodeEnum.API_STATUS_CODE_31.getCode()), ApiStatusCodeEnum.API_STATUS_CODE_31.getMessage());
                }
            } else {
                jsonBean = new JsonBean(Integer.valueOf(ApiStatusCodeEnum.API_STATUS_CODE_31.getCode()), ApiStatusCodeEnum.API_STATUS_CODE_31.getMessage());
            }
        } catch (Exception e) {
            log.error("验证签名失败rootVariables:[{}]", JSON.toJSONString(rootVariables), e);
            jsonBean = new JsonBean(Integer.valueOf(ApiStatusCodeEnum.API_STATUS_CODE_31.getCode()), ApiStatusCodeEnum.API_STATUS_CODE_31.getMessage());
        }
        return jsonBean;
    }

    private boolean checkTimestamp(long timestamp) {
        Date requestDate = DateUtil.date(timestamp);
        Date nowDate = new Date();
        long diffSecond = DateUtil.between(requestDate, nowDate, DateUnit.SECOND);
        if (diffSecond > maxTimeDelay) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 接口执行之后
     *
     * @param info    接口信息
     * @param context 变量信息
     * @param value   即将要返回到页面的值
     */
    @Override
    public Object postHandle(ApiInfo info, MagicScriptContext context, Object value, HttpServletRequest request, HttpServletResponse response) throws Exception {
        log.debug("{} 执行完毕，返回结果:{}", info.getName(), value);
        return null;
    }

    public List<OpenAuthAccountInfo> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<OpenAuthAccountInfo> accounts) {
        this.accounts = accounts;
    }
}
